﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using BloggerAutoPoster.Common.Mvvm;
using BloggerAutoPoster.Common.Toolkit;
using BloggerAutoPoster.Infrastructure.AlertConfirmBox;
using BloggerAutoPoster.Infrastructure.Models;
using BloggerAutoPosterService.Engine.Core;
using BloggerAutoPosterService.Engine.Models;
using BloggerAutoPosterService.Engine.Net;
using BloggerAutoPosterService.Engine.Utils;

namespace BloggerAutoPoster.Infrastructure.ViewModels
{
    public partial class PostsListViewModel : ViewModelBase
    {
        #region Fields

        string _searchData;
        TaskScheduler _uiSynchronizationContext;
        bool _canStartJob = true;
        readonly SortedSet<string> _chagedItems = new SortedSet<string>();
        NewsItems _newsItemsDataInternal;
        private IRedirectFinder _redirectFinder;
        ScheduledJobRunner _scheduledJobRunner;
        private IServiceConfig _serviceConfig;

        #endregion Fields

        #region Constructors (1)

        public PostsListViewModel()
        {
            setupItemsData();
            setupCommands();
            setupMessenger();
            loadDb();
            startJobs();
            detectNetwrokChanges();
        }

        #endregion Constructors

        #region Properties (15)

        public DelegateCommand<object> DoCfg { set; get; }

        public DelegateCommand<string> DoCopyAll { set; get; }

        public DelegateCommand<string> DoCopyThisUrlToClipboard { set; get; }

        public DelegateCommand<object> DoDelete { set; get; }

        public DelegateCommand<string> DoMarkAllNewsAsSent { set; get; }

        public DelegateCommand<string> DoOpenThisFile { set; get; }

        public DelegateCommand<string> DoPostAll { set; get; }

        public DelegateCommand<string> DoSave { set; get; }

        public DelegateCommand<string> DoSaveChanges { set; get; }

        public DelegateCommand<object> DoSelectAll { set; get; }

        public DelegateCommand<string> DoSelectUnsentNews { set; get; }

        public DelegateCommand<string> DoStart { set; get; }

        private NewsItems newsItemsDataInternal
        {
            set
            {
                _newsItemsDataInternal = value;
                if (NewsItemsDataView != null)
                {
                    initView(value);
                    RaisePropertyChanged("NewsItemsDataView");
                }
            }
            get { return _newsItemsDataInternal; }
        }

        public ICollectionView NewsItemsDataView { set; get; }

        public PostsListGui PostsListGuiData { set; get; }

        #endregion Properties

        #region Methods (28)

        // Private Methods (28) 

        private void detectNetwrokChanges()
        {
            NetworkChange.NetworkAddressChanged += networkChangeNetworkAddressChanged;
            NetworkChange.NetworkAvailabilityChanged += networkChangeNetworkAvailabilityChanged;
        }

        void doCfg(object data)
        {
            PostsListGuiData.PopupDoCfgIsOpen = true;
        }

        void doCloseCfgView()
        {
            PostsListGuiData.PopupDoCfgIsOpen = false;
            _serviceConfig = new ServiceConfig(useApplicationPath: true);
            _scheduledJobRunner.OnStop();
            _scheduledJobRunner = new ScheduledJobRunner(_redirectFinder, _serviceConfig);
        }

        void doCopyAll(string data)
        {
            var html = getSelectedItemsHtmlString();
            if (string.IsNullOrWhiteSpace(html)) return;
            html.ClipboardSetText();
        }

        void doCopyThisUrlToClipboard(string data)
        {
            if (PostsListGuiData.SelectedItem == null) return;
            PostsListGuiData.SelectedItem.Link.ClipboardSetText();
        }

        void doDelete(object data)
        {
            if (!PostsListGuiData.NewsItemsList.Any(x => x.IsSelected)) return;
            Task.Factory.StartNew(() => runTask(() =>
            {
                var itemsToDelete = PostsListGuiData.NewsItemsList.Where(x => x.IsSelected);
                new XmlDB(_serviceConfig).DeleteRecords(itemsToDelete);
                foreach (var item in itemsToDelete.ToList())
                    PostsListGuiData.NewsItemsList.Remove(item);
            }));
        }

        void doMarkAllNewsAsSent(string data)
        {
            if (PostsListGuiData.NewsItemsList == null || !PostsListGuiData.NewsItemsList.Any()) return;
            new XmlDB(_serviceConfig).MarkAsSent(PostsListGuiData.NewsItemsList);
            loadDb();
        }

        void doOpenThisFile(string data)
        {
            if (PostsListGuiData.SelectedItem == null) return;
            Process.Start(new Uri(PostsListGuiData.SelectedItem.Link).AbsoluteUri);
        }

        void doPostAll(string data)
        {
            Task.Factory.StartNew(() => runTask(() =>
            {
                var body = getSelectedItemsHtmlString();
                if (string.IsNullOrWhiteSpace(body)) return;

                var date = !_serviceConfig.UsePersianDate
                           ? DateTime.Now.ToString("dd-MM-yyyy")
                           : DateTime.Now.ToPersianDateTimeString();

                new RunAll(_serviceConfig, _redirectFinder).PostNews(body, date);
                new XmlDB(_serviceConfig).MarkAsSent(PostsListGuiData.NewsItemsList.Where(x => !x.Sent));
                loadDb();
            }));
        }

        void doSave(string data)
        {
            var html = getSelectedItemsHtmlString();
            if (string.IsNullOrWhiteSpace(html)) return;
            if (string.IsNullOrWhiteSpace(PostsListGuiData.SaveFilePath)) return;
            File.WriteAllText(PostsListGuiData.SaveFilePath, html);
        }

        void doSaveChanges(string data)
        {
            if (!_chagedItems.Any() || PostsListGuiData.NewsItemsList == null || !PostsListGuiData.NewsItemsList.Any()) return;
            Task.Factory.StartNew(() => runTask(() =>
            {
                var chagedItems = PostsListGuiData.NewsItemsList.Where(x => _chagedItems.Contains(x.UrlHash)).ToList();
                new XmlDB(_serviceConfig).EditRecords(chagedItems);
                _chagedItems.Clear();
            }));
        }

        private void doSearch(string data)
        {
            _searchData = data;
            reCalculatePagerItems(_newsItemsDataInternal.Count);
            NewsItemsDataView.Filter = viewFilter;
        }

        void doSelectAll(object isChecked)
        {
            if (PostsListGuiData.NewsItemsList == null || !PostsListGuiData.NewsItemsList.Any()) return;
            foreach (var item in PostsListGuiData.NewsItemsList)
            {
                item.IsSelected = (bool)isChecked;
            }
        }

        void doSelectUnsentNews(string data)
        {
            if (PostsListGuiData.NewsItemsList == null || !PostsListGuiData.NewsItemsList.Any()) return;
            foreach (var item in PostsListGuiData.NewsItemsList.Where(x => !x.Sent))
            {
                item.IsSelected = true;
            }
        }

        void doStart(string data)
        {
            startJob();
        }

        private string getSelectedItemsHtmlString()
        {
            if (PostsListGuiData.NewsItemsList == null || !PostsListGuiData.NewsItemsList.Any(x => x.IsSelected)) return string.Empty;
            return new ExportData(_serviceConfig).ListToString(PostsListGuiData.NewsItemsList.Where(x => x.IsSelected).ToList());
        }

        private void loadDb()
        {
            if (Designer.IsInDesignModeStatic) return;
            Task.Factory.StartNew(() => runTask(() =>
            {
                if (!File.Exists(_serviceConfig.DBName)) return;
                var list = new XmlDB(_serviceConfig).ListNewsRecords(includeDeletedRecords: false).ToList();
                PostsListGuiData.NewsItemsList = new NewsItems(list);
                newsItemsDataInternal = PostsListGuiData.NewsItemsList;
                newsItemsDataInternal.OnItemPropertyChanged = onItemPropertyChanged;
                initView(newsItemsDataInternal);
            })).ContinueWith(t => PostsListGuiData.ScrollToIndex = newsItemsDataInternal.Count - 1, _uiSynchronizationContext);
        }

        void networkChangeNetworkAddressChanged(object sender, EventArgs e)
        {
            tryConnect();
        }

        void networkChangeNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            tryConnect();
        }

        void onItemPropertyChanged(object data)
        {
            if (data == null) return;
            _chagedItems.Add(((NewsRecord)data).UrlHash);
        }

        void postsListGuiDataPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "SearchText":
                    doSearch(PostsListGuiData.SearchText.ApplyUnifiedYeKe());
                    break;
            }
        }

        void runTask(Action task)
        {
            try
            {
                PostsListGuiData.IsBusy = true;
                PostsListGuiData.IsEnabled = false;
                task();
                PostsListGuiData.IsBusy = false;
            }
            catch (Exception ex)
            {
                new Logger(_serviceConfig).WriteLine(ex);
                SendMsg.ShowMsg(new AlertConfirmBoxModel
                {
                    ErrorTitle = "Error",
                    Errors = new List<string> { ex.Message },
                    ShowCancel = Visibility.Hidden,
                    ShowConfirm = Visibility.Visible
                });
            }
            finally
            {
                PostsListGuiData.IsBusy = false;
                PostsListGuiData.IsEnabled = true;
            }
        }

        private void setupCommands()
        {
            DoDelete = new DelegateCommand<object>(doDelete, canDoJobs);
            DoSelectAll = new DelegateCommand<object>(doSelectAll, obj => true);
            DoStart = new DelegateCommand<string>(doStart, obj => true);
            DoSelectUnsentNews = new DelegateCommand<string>(doSelectUnsentNews, obj => true);
            DoMarkAllNewsAsSent = new DelegateCommand<string>(doMarkAllNewsAsSent, obj => true);
            DoSave = new DelegateCommand<string>(doSave, canDoJobs);
            DoOpenThisFile = new DelegateCommand<string>(doOpenThisFile, obj => true);
            DoCopyThisUrlToClipboard = new DelegateCommand<string>(doCopyThisUrlToClipboard, canDoJobs);
            DoPostAll = new DelegateCommand<string>(doPostAll, canDoJobs);
            DoCopyAll = new DelegateCommand<string>(doCopyAll, canDoJobs);
            DoCfg = new DelegateCommand<object>(doCfg, obj => true);
            DoSaveChanges = new DelegateCommand<string>(doSaveChanges, canDoSaveChanges);
        }

        bool canDoSaveChanges(string data)
        {
            return _chagedItems.Any();
        }

        bool canDoJobs(object data)
        {
            return PostsListGuiData.NewsItemsList != null && PostsListGuiData.NewsItemsList.Any(x => x.IsSelected);
        }

        private void setupItemsData()
        {
            _uiSynchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
            _serviceConfig = new ServiceConfig(useApplicationPath: true);
            _redirectFinder = new RedirectFinder();
            ProxySettings.SetDefaultProxy(_serviceConfig);
            PostsListGuiData = new PostsListGui();
            PostsListGuiData.PropertyChanged += postsListGuiDataPropertyChanged;
            newsItemsDataInternal = PostsListGuiData.NewsItemsList;
            pagerInit();
            initView(newsItemsDataInternal);
        }

        private void initView(NewsItems newsItems)
        {
            NewsItemsDataView = CollectionViewSource.GetDefaultView(newsItems);
            if (NewsItemsDataView.GroupDescriptions.Count == 0)
            {
                NewsItemsDataView.GroupDescriptions.Clear();
                NewsItemsDataView.GroupDescriptions.Add(new PropertyGroupDescription("AddDate"));
            }
            reCalculatePagerItems(newsItems.Count);
            NewsItemsDataView.Filter = viewFilter;
        }

        private void setupMessenger()
        {
            App.Messenger.Register("doCloseCfgView", doCloseCfgView);
        }

        private void startJob()
        {
            if (!_canStartJob) return;
            Task.Factory.StartNew(() =>
                 {
                     _canStartJob = false;
                     runTask(() => new RunAll(_serviceConfig, _redirectFinder).ReadFeedSaveData(throwException: true));
                 })
                 .ContinueWith(t =>
                     {
                         _canStartJob = true;
                         loadDb();
                     }, _uiSynchronizationContext);
        }

        private void startJobs()
        {
            _scheduledJobRunner = new ScheduledJobRunner(_redirectFinder, _serviceConfig);
            _scheduledJobRunner.OnStart();
        }

        private void tryConnect()
        {
            if (!NetworkInterface.GetIsNetworkAvailable()) return;

            Thread.Sleep(new TimeSpan(0, 2, 0));
            startJob();
        }

        #endregion Methods
    }
}
